package com.zxl.designpattern.creator.prototype;

import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.*;

/**
 * @author zxl
 * @version 1.0
 * @description 工牌   设计模式-创建型模式-原型模式（深复制）
 *  用一个已经创建的实例作为原型，通过复制该原型对象来创建一个和原型对象相同的新对象
 *  抽象原型类：规定了具体原型对象必须实现的的 clone() 方法。
 *  具体原型类：实现抽象原型类的 clone() 方法，它是可被复制的对象。Java类规定了Cloneable接口，实现了该接口的类可以被复制。
 *  访问类：使用具体原型类中的 clone() 方法来复制新的对象。
 *
 *  浅克隆：创建一个新对象，新对象的属性和原来对象完全相同，对于非基本类型属性，仍指向原有属性所指向的对象的内存地址。
 *  深克隆：创建一个新对象，属性中引用的其他对象也会被克隆，不再指向原有对象地址。
 *
 *  使用场景：对象的创建非常复杂，可以使用原型模式快捷的创建对象。
 *          性能和安全要求比较高。
 * @date 2025/5/31 22:47
 */
public class WorkCard implements Cloneable, Serializable{

    /**
     * 克隆类型。这里仅为方便，省去每种复制方法写一个类的麻烦。实际上业务上不会这么写。
     */
    public int cloneType = 1;

    /**
     * 员工编号。
     */
    private String id;

    /**
     *  公司名称
     */
    private String compName;

    /**
     *  员工
     */
    private Employee employee;

    public WorkCard() {}
    public WorkCard(String id, String compName, Employee employee) {
        this.id = id;
        this.compName = compName;
        this.employee = employee;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getCompName() {
        return compName;
    }

    public void setCompName(String compName) {
        this.compName = compName;
    }

    public Employee getEmployee() {
        return employee;
    }

    public void setEmployee(Employee employee) {
        this.employee = employee;
    }

    @Override
    public String toString() {
        return "WorkCardDeepClone=" + super.toString() + "{" +
                "id='" + id + '\'' +
                ", compName='" + compName + '\'' +
                ", employee=" + employee +
                '}';
    }

    /**
     * 克隆方法
     * @return
     * @throws CloneNotSupportedException
     */
    public WorkCard clone() throws CloneNotSupportedException {

        switch (cloneType){
            case 1:
                return deepCloneWithManual();
            case 2:
                return deepCloneWithIO();
            case 3:
                return deepCloneWithJson();
            default:
                return (WorkCard) super.clone();
        }
    }

    /**
     * 深克隆方法：每个对象都要实现Cloneable接口，并重写clone()方法，并且在这里手动进行每个对象的克隆
     * 如果层级深的话，代码会很复杂，而且如果有新的对象加入，也需要修改clone()方法
     * @return
     * @throws CloneNotSupportedException
     */
    private WorkCard deepCloneWithManual() throws CloneNotSupportedException {
        WorkCard workCardDeepClone = (WorkCard) super.clone();
        workCardDeepClone.employee = workCardDeepClone.employee.clone();
        return workCardDeepClone;
    }

    /**
     * 深克隆方法：使用对象流复制对象，不需要每个对象都实现Cloneable接口，但需要每个对象都实现Serializable接口
     * IO流因为出了方法对象会被回收，所以不close实际也会被回收。但是为了规范，还是要close。
     * 这里用不了try-with-resources，因为需要先关闭输出流，再关闭输入流。如果用了，会先关闭输入流，再关闭输出流，会报错EOF
     * @return
     * @throws CloneNotSupportedException
     */
    private WorkCard deepCloneWithIO() throws CloneNotSupportedException {
        try {
            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bytes);
            oos.writeObject(this);
            oos.close();  // 先关闭输出流

            ByteArrayInputStream bis = new ByteArrayInputStream(bytes.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            return (WorkCard) ois.readObject();

        } catch (IOException | ClassNotFoundException e) {
            throw new CloneNotSupportedException("深克隆失败: " + e.getMessage());
        }
    }

    /**
     * 深克隆方法：使用Json序列化和反序列化复制对象，不需要每个对象都实现Cloneable接口
     * @return
     * @throws CloneNotSupportedException
     */
    private WorkCard deepCloneWithJson() throws CloneNotSupportedException {
        ObjectMapper mapper = new ObjectMapper();
        try {
            String s = mapper.writeValueAsString(this);
            System.out.println("json：" + s);
            return mapper.readValue(s, WorkCard.class);
        } catch (IOException e) {
            throw new CloneNotSupportedException("深克隆失败: " + e.getMessage());
        }
    }

    /**
     * 深克隆方法：使用构造器复制。仅写出注释代码举例，实际实现比第一种还麻烦。而且和clone方法也没什么关系了
     * class Address {
     *     private String street;
     *     public Address(String street) { this.street = street; }
     *     public Address(Address other) { this.street = other.street; }
     * }
     *
     * class Person {
     *     private Address address;
     *     public Person(Address address) { this.address = address; }
     *     public Person(Person other) {
     *         this.address = new Address(other.address); // 复制引用类型
     *     }
     * }
     */
}
